package cn.zcltd.btg.set.sourcemanager.db;

import cn.zcltd.btg.set.ParamSource;
import cn.zcltd.btg.set.ParamType;
import cn.zcltd.btg.set.core.Config;
import cn.zcltd.btg.set.core.Module;
import cn.zcltd.btg.set.core.Param;
import cn.zcltd.btg.set.exception.BtgSetRuntimerException;
import cn.zcltd.btg.set.sourcemanager.SourceManager;
import cn.zcltd.btg.set.sourcemanager.db.dialect.Dialect;
import cn.zcltd.btg.set.sourcemanager.db.dialect.MySQLDialect;
import cn.zcltd.btg.set.util.DbUtil;
import cn.zcltd.btg.set.util.StringUtil;
import cn.zcltd.btg.sutil.EmptyUtil;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 关系数据库配置管理器
 */
public class DbSourceManager extends SourceManager {

    private DataSource dataSource;
    private Dialect dialect;
    private DbSourceManagerConfig dbSourceManagerConfig;

    public DbSourceManager(String name, DataSource dataSource) {
        super(name);
        this.dataSource = dataSource;
        this.dialect = new MySQLDialect();
        this.dbSourceManagerConfig = new DbSourceManagerConfig();
    }

    public DbSourceManager(String name, DataSource dataSource, Dialect dialect) {
        super(name);
        this.dataSource = dataSource;
        this.dialect = dialect;
        this.dbSourceManagerConfig = new DbSourceManagerConfig();
    }

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public Dialect getDialect() {
        return dialect;
    }

    public void setDialect(Dialect dialect) {
        this.dialect = dialect;
    }

    public DbSourceManagerConfig getDbSourceManagerConfig() {
        return dbSourceManagerConfig;
    }

    public void setDbSourceManagerConfig(DbSourceManagerConfig dbSourceManagerConfig) {
        this.dbSourceManagerConfig = dbSourceManagerConfig;
    }

    /**
     * 初始化数据表
     */
    @Override
    public void init(boolean devModel) {
        try {
            log.debug("init table...");

            setDevMode(devModel);

            Connection conn = dataSource.getConnection();
            conn.setAutoCommit(false);

            String tablePrefix = dbSourceManagerConfig.getTablePrefix();

            //初始化config表
            String configTableName = tablePrefix + "_" + dbSourceManagerConfig.getConfigTableName();
            if (!dialect.tableIsExists(conn, configTableName)) {
                dialect.createTableConfig(conn, configTableName);
            }

            //初始化module表
            String moduleTableName = tablePrefix + "_" + dbSourceManagerConfig.getModuleTabletable();
            if (!dialect.tableIsExists(conn, moduleTableName)) {
                dialect.createTableModule(conn, moduleTableName);
            }

            //初始化param表
            String paramTableName = tablePrefix + "_" + dbSourceManagerConfig.getParamTableName();
            if (!dialect.tableIsExists(conn, paramTableName)) {
                dialect.createTableParam(conn, paramTableName);
            }

            conn.commit();
            DbUtil.closeConnection(conn);

            List<Config> configs = loadAll();
            for (Config config : configs) {
                getCache().put(config.getId(), config);
            }

            log.debug("init table success");
        } catch (SQLException e) {
            log.error(e.getMessage(), e);
        }
    }

    @Override
    public List<Config> loadAll() {
        List<Config> configs = new ArrayList<Config>();
        try {
            Connection conn = dataSource.getConnection();
            conn.setAutoCommit(false);

            List<Map<String, Object>> configList = dialect.listConfig(conn, dbSourceManagerConfig);
            for (Map<String, Object> configMap : configList) {
                String configId = String.valueOf(configMap.get("config_id"));
                Config config = load(configId);
                configs.add(config);
            }

            conn.commit();
            DbUtil.closeConnection(conn);
        } catch (SQLException e) {
            log.error(e.getMessage(), e);
        }
        return configs;
    }

    @Override
    public Config load(String configId) {
        Config config = null;
        try {
            Connection conn = dataSource.getConnection();
            conn.setAutoCommit(false);

            /*
                加载config
             */
            Map<String, Object> configMap = dialect.mapConfig(conn, configId, dbSourceManagerConfig);
            if (null == configMap) {
                throw new BtgSetRuntimerException(configId + " not exists");
            }

            config = Config.createConfig(configId, String.valueOf(configMap.get("config_name")));
            config.setDesc(String.valueOf(configMap.get("config_desc")));

            /*
                加载config>param
             */
            List<Map<String, Object>> configParamList = dialect.listConfigParam(conn, configId, dbSourceManagerConfig);
            for (Map<String, Object> configParamMap : configParamList) {
                String paramId = String.valueOf(configParamMap.get("param_id"));
                String paramType = String.valueOf(configParamMap.get("param_type"));
                String paramDatePattern = String.valueOf(configParamMap.get("param_date_pattern"));
                String paramName = String.valueOf(configParamMap.get("param_name"));
                String paramValue = String.valueOf(configParamMap.get("param_value"));
                String paramDesc = String.valueOf(configParamMap.get("param_desc"));
                String paramRemark = String.valueOf(configParamMap.get("param_remark"));

                Param param = config.createParam(paramId, paramName, ParamType.valueOf(paramType), paramValue);
                if (EmptyUtil.isNotEmpty(paramDatePattern)) {
                    param.setDatePattern(paramDatePattern);
                }
                if (EmptyUtil.isNotEmpty(paramDesc)) {
                    param.setDesc(paramDesc);
                }
                if (EmptyUtil.isNotEmpty(paramRemark)) {
                    param.setRemark(paramRemark);
                }
            }

            /*
                加载config>module
             */
            List<Map<String, Object>> configModuleList = dialect.listConfigModule(conn, configId, dbSourceManagerConfig);
            for (Map<String, Object> configModuleMap : configModuleList) {
                String moduleId = String.valueOf(configModuleMap.get("module_id"));
                String moduleName = String.valueOf(configModuleMap.get("module_name"));
                String moduleDesc = String.valueOf(configModuleMap.get("module_desc"));

                Module module = config.createModule(moduleId, moduleName);
                if (EmptyUtil.isNotEmpty(moduleDesc)) {
                    module.setDesc(moduleDesc);
                }

                /*
                    加载config>module>param
                 */
                List<Map<String, Object>> moduleParamList = dialect.listModuleParam(conn, config.getId(), module.getId(), dbSourceManagerConfig);
                for (Map<String, Object> moduleParamMap : moduleParamList) {
                    String paramId = String.valueOf(moduleParamMap.get("param_id"));
                    String paramType = String.valueOf(moduleParamMap.get("param_type"));
                    String paramDatePattern = String.valueOf(moduleParamMap.get("param_date_pattern"));
                    String paramName = String.valueOf(moduleParamMap.get("param_name"));
                    String paramValue = String.valueOf(moduleParamMap.get("param_value"));
                    String paramDesc = String.valueOf(moduleParamMap.get("param_desc"));
                    String paramRemark = String.valueOf(moduleParamMap.get("param_remark"));

                    Param param = module.createParam(paramId, paramName, ParamType.valueOf(paramType), paramValue);
                    if (EmptyUtil.isNotEmpty(paramDatePattern)) {
                        param.setDatePattern(paramDatePattern);
                    }
                    if (EmptyUtil.isNotEmpty(paramDesc)) {
                        param.setDesc(paramDesc);
                    }
                    if (EmptyUtil.isNotEmpty(paramRemark)) {
                        param.setRemark(paramRemark);
                    }
                }
            }

            conn.commit();
            DbUtil.closeConnection(conn);
        } catch (SQLException e) {
            log.error(e.getMessage(), e);
        }
        return config;
    }

    @Override
    public void save(Config config) {
        try {
            Connection conn = dataSource.getConnection();
            conn.setAutoCommit(false);

            /*
                删除原有配置
             */
            dialect.clearConfig(conn, config.getId(), dbSourceManagerConfig);

            /*
                保存config
             */
            if (dialect.insertConfig(conn, config, dbSourceManagerConfig) == 0) {
                throw new BtgSetRuntimerException("insert " + config.getId() + " failure");
            }

            /*
                保存config>param
             */
            for (Param param : config.getParams()) {
                if (dialect.insertParam(conn, ParamSource.CONFIG, param, dbSourceManagerConfig) == 0) {
                    throw new BtgSetRuntimerException("insert " + param.getPath() + " failure");
                }
            }

            /*
                保存config>module
             */
            for (Module module : config.getModules()) {
                if (dialect.insertModule(conn, module, dbSourceManagerConfig) == 0) {
                    throw new BtgSetRuntimerException("insert " + module.getPath() + " failure");
                }

                /*
                    保存config>module>param
                 */
                for (Param param : module.getParams()) {
                    if (dialect.insertParam(conn, ParamSource.MODULE, param, dbSourceManagerConfig) == 0) {
                        throw new BtgSetRuntimerException("insert " + param.getPath() + " failure");
                    }
                }
            }

            conn.commit();
            DbUtil.closeConnection(conn);
        } catch (SQLException e) {
            log.error(e.getMessage(), e);
        }
    }
}